global listaCrear
global listaInsertar
global listaDestruir
global listaImprimir
global listaFilter
global listaMap
global es_multiplo_de_5
global es_negativo
global es_largo_mayor_10
global tomar_primeros_10
global dividir_por_dos
global multiplicar_por_pi

extern malloc
extern free
extern strcpy
extern strncpy
extern strlen
extern fopen
extern fclose
extern fprintf

section .rodata

%define NULL 0
%define tamanio_lista 12	; 4 del int del tipo y 8 del puntero al primer elemento
%define tamanio_nodo 16		; 8 del puntero al siguiente y 8 del puntero al dato
%define tamanio_char 1		; tamanio de char
%define tamanio_int 4		; tamanio de int
%define tamanio_double 8	; tamanio de double
%define offset_tipo_dato 0	; offset de la lista al tipo de dato
%define offset_primero 4	; offset de lista al puntero del primer elemento
%define offset_siguiente 0	; offset del nodo al puntero del siguiente nodo
%define offset_dato 8		; offset de nodo al puntero del dato



section .data

formato_int: DB '[%d]', 0
formato_double: DB '[%f]', 0
formato_string: DB '[%s]', 0
formato_fin_linea: DB '%s', 0
fin_linea: DB 10, 0
file_mode: db "a+", 0
pi: DQ 3.14159265

section .text

; ============ lista * listaCrear()
listaCrear:

;###### pido memoria para el struct lista
	mov RDI, tamanio_lista		; le paso el sizeof al malloc
	sub RSP, 8			; alineo la pila
	call malloc			; llamo a malloc
	cmp RAX, NULL			; chequeo que malloc no me devuelva null
	je .fin_error			; si me devolvio null me voy devolviendo error
	add RSP, 8			; vuelvo la pila a su estado
	mov RDI, RAX			; preservo el puntero a la lista
;###### seteo en null el puntero al primero y en 0 el tipo de dato
	mov dword [RAX], 0		; pongo en 0 el tipo de dato
	lea RAX, [RAX + offset_primero]	; me muevo al puntero al primero			
	mov qword [RAX], NULL		; pongo en null el puntero al primero
	mov RAX, RDI			; vuelvo a apuntar rax al comienzo del struct lista
	jmp .fin

.fin_error:
	mov RAX, 1
	jmp .fin

.fin:	ret







; ============ void listaInsertar(lista * l, enum tipo e, void* dato);
;				      RDI/R14          RSI	 RDX/R13
listaInsertar:
;######	en R15 voy a guardar el puntero al siguiente del nuevo, es decir, si es el primero sera 0 sino el ex l->primero	
;######	en R14 preservo el puntero a la lista
	;push RBP			; impar
	;mov RBP, RSP			; comienzo a armar el stackframe
	push R15			; impar | preservo R15
	push R14			; par | preservo R14
	push R13			; impar | preservo R13
	mov R13, RDX			; preservo el puntero al dato
	mov R14, RDI			; en R14 preservo el puntero a la lista
	cmp qword [RDI], 0		; me fijo si el primero apunta a null, osea si es vacia o no
	jne .noVacia			; si no es 0 es que no es vacia, me voy para .noVacia
	lea RDI, [RDI + offset_tipo_dato]	; al ser el primero seteo el tipo de dato, me posiciono ahi
	mov [RDI], ESI			; seteo el tipo de dato
	mov R15, NULL			; seteo el puntero a siguiente en null
	jmp .encadenar

;######	no es el primero, chequeo el tipo de dato y seteo el puntero a siguiente
.noVacia:
	lea RDI, [RDI + offset_tipo_dato]	; me posiciono en el tipo de dato
	cmp dword [RDI], ESI			; comparo el tipo de la lista y el que viene por parametro
	jne .fin				; si son distintos me voy
	lea RDI, [R14 + offset_primero]		; me posiciono en el puntero al primero
	mov R15, [RDI]				; seteo en R15 el puntero a siguente

.encadenar:
	mov RDI, tamanio_nodo			; le paso a malloc el tamaño de un nuevo nodo
	call malloc				; llamo a malloc
	cmp RAX, NULL				; chequeo que malloc no me devuelva null
	je .fin_error				; si me devolvio null me voy devolviendo error
	lea RDI, [R14 + offset_primero]		; me posiciono en el puntero al primero
	mov [RDI], RAX				; seteo como puntero a primero el nuevo nodo
	lea RAX, [RAX + offset_siguiente]	; me posiciono en el puntero a siguiente del nuevo nodo
	mov [RAX], R15				; en R15 tenia el puntero a siguiente, lo seteo
	lea R15, [RAX - offset_siguiente]	; ahora en R15 voy a dejar el puntero al nodo nuevo

;######	para setear el dato me fijo el tipo de dato de la lista y en base a eso actuo
;######	hasta el momento tengo: en R14 el puntero a la lista,en R13 el puntero al dato y en R15 el puntero al nuevo nodo
	mov ESI, [R14 + offset_tipo_dato]
	cmp dword ESI, 1			; me fijo si es un int
	je .set_int				; me voy a setear el int
	cmp dword ESI, 2			; me fijo si es un double
	je .set_double				; me voy a setear el double
	cmp dword ESI, 3			; me fijo si es un string
	je .set_string				; me voy a setear el string
	jmp .fin_error				; si no entro en ningun caso hubo un error en el medio

.set_int:
	mov RDI, tamanio_int			; le paso el tamaño a malloc
	call malloc				; llamo a malloc
	cmp RAX, NULL				; me fijo si es null lo que me devolvio malloc
	je .fin_error				; si era null me voy con error
	lea R15, [R15 + offset_dato]		; me posiciono apuntando al dato del nuevo nodo
	mov [R15], RAX				; seteo el puntero al dato
	mov EDX, [R13]				; me copio el dato int a EDX
	mov [RAX], EDX				; copio el dato al nodo
	jmp .fin
	

.set_double:
	mov RDI, tamanio_double			; le paso el tamaño a malloc
	call malloc				; llamo a malloc
	cmp RAX, NULL				; me fijo si es null lo que me devolvio malloc
	je .fin_error				; si era null me voy con error
	lea R15, [R15 + offset_dato]		; me posiciono apuntando al dato del nuevo nodo
	mov [R15], RAX				; seteo el puntero al dato
	movq XMM0, [R13]			; me copio el dato int a XMM0
	movq [RAX], XMM0			; copio el dato al nodo
	jmp .fin


.set_string:
	mov RDI, R13				; le paso a strlen el puntero al dato
	call strlen				; calculo el largo del string dato
	mov RBX, RAX				; preservo el tamaño en RBX 
	;mul RBX, tamanio_char			; multiplico la cantidad de bytes del dato
	mov RDI, RBX				; como voy a hacer malloc se lo paso a RDI
	add RDI, 1				; le sumo uno por el '\0'
	call malloc				; llamo a malloc
	cmp RAX, NULL				; me fijo si es null lo que me devolvio malloc
	je .fin_error				; si era null me voy con error
	lea R15, [R15 + offset_dato]		; me posiciono apuntando al dato del nuevo nodo
	mov [R15], RAX				; seteo el puntero al dato
	mov RDI, RAX				; como voy a hacer strcpy le paso el nuevo puntero a dato 
	mov RSI, R13				; y el dato original 
	call strcpy				; copio el string
	lea RAX, [RAX + RBX]			; me posiciono al final del string
	mov byte [RAX], 0			; escribo el fin del string
	jmp .fin			

.fin_error:
	mov RAX, 1
	jmp .fin

.fin:	
	pop R13
	pop R14
	pop R15
	;pop RBP
	ret


; ============ void  listaDestruir(lista * l)
;					  RDI
listaDestruir:
	push R15			; impar | preservo lo que tenia R15
	mov R15, RDI			; guardo el puntero a la lista en R15
	mov R15, [R15 + offset_primero]	; apunto R15 al puntero al primer nodo
	call free			; libero la memoria donde estaba el struct lista
	
;######	recorro la lista hasta encontrar un siguiente que sea null, ese seria el ultimo

.ciclo:	cmp qword R15, 0		; comparo si el puntero al nodo es null
	je .fin				; si es null termine de borrar, me voy
	mov RDI, [R15 + offset_dato]	; le paso a free el puntero al dato
	call free			; libero el dato
	mov RDI, R15			; paso el puntero del nodo actual a free
	mov R15, [RDI]			; me guardo el puntero al siguente
	call free			; libero memoria
	jmp .ciclo			; me voy a preguntar si todavia hay mas nodos

.fin:	pop R15
	ret




; ============ void  listaImprimir(lista  * l, char *archivo)
;					   RDI		RSI
listaImprimir:

	push R15				; impar | preservo R15
	push R14				; par | preservo R14
	push RBX				; impar | preservo R8

	xor RBX, RBX
	mov EBX, [RDI + offset_tipo_dato] 	; seteo RBX apuntando al tipo de dato (es 0 ese offset)
	mov RDI, [RDI + offset_primero]		; me posiciono apuntando al primer nodo
	;cmp RDI, NULL				; me fijo si es vacia
	;je .fin					; si el primer nodo es null esta vacia, me voy		
	mov R15, RDI				; me guardo el puntero al primer nodo en R15
	mov RDI, RSI				; le paso el archivo a fopen
	mov RSI, file_mode			; le paso el modo a fopen
	call fopen				; llamo a fopen
	cmp RAX, NULL				; chequeo que fopen no me retorne null
	je .fin_error				; si es asi salgo con error
	mov R14, RAX				; me guardo el file en R14

.ciclo:
	cmp R15, NULL
	je .fin_de_linea

.es_int:
	cmp EBX, 1				; me fijo si es un int
	jne .es_double				; sigo preguntando por el tipo de dato
	mov RSI, formato_int			; seteo en RSI el formato de int
	mov RDX, [R15 + offset_dato]		; me posiciono en el puntero al dato
	mov EDX, [RDX]				; seteo el dato
	jmp .continuo				; dato seteado, sigo
.es_double:
	cmp EBX, 2				; me fijo si es un double
	jne .es_string				; sigo preguntando por el tipo de dato
	mov RSI, formato_double			; seteo en RSI el formato de double
	mov RDX, [R15 + offset_dato]		; me posiciono en el puntero al dato
	movq XMM0, [RDX]			; seteo el dato
	jmp .continuo				; dato seteado, sigo
.es_string:
	cmp EBX, 3				; me fijo si es un string
	jne .fin_error				; si no es hubo un error con el tipo de dato
	mov RSI, formato_string			; seteo en RSI el formato de string
	mov RDX, [R15 + offset_dato]		; me posiciono en el puntero al dato

.continuo:
	mov RDI, R14				; paso el file como parametro
	mov RAX, 1				; convencion
	call fprintf				; imprimo en el archivo
	mov R15, [R15 + offset_siguiente]	; nodoActual = nodoActual->siguiente
	jmp .ciclo

		

;###### pongo el fin de linea
.fin_de_linea:
	mov RDX, fin_linea			; paso el dato como parametro
	mov RSI, formato_fin_linea		; paso el formato como parametro
	mov RDI, R14				; paso el file como parametro	
	mov RAX, 1
	call fprintf
	mov RDI, R14				; le paso el archivo a fclose
	call fclose				; cierro el archivo y me las pico

.fin_error:
	mov RAX, 1
	jmp .fin

.fin:
	pop RBX
	pop R14
	pop R15
	ret



; ============ lista  listaFilter(lista l, void* (*funcion_filter)(void*) )
;					RDI		RSI
;nodoActual: R15
;nodoAnterior: R14
;nodoAux: R13
;funcion_filter: R12

listaFilter:
	push R15		; impar | preservo R15
	push R14		; par | preservo R14
	push R13		; impar | preservo R13
	push R12		; par | preservo R12
	push RBX		; impar | preservo RBX

	mov R15, [RDI + offset_primero]	; en nodoActual apunto al primero
	mov R14, NULL			; nodoAnterior es NULL, en el unico momento que debiera ser NULL
	mov R13, NULL 			; nodoAux NULL por el momento
	mov R12, RSI			; en R12 guardo la posicion en memoria de la funcion_filter
	lea RBX, [RDI + offset_primero]	; a priori guardo el puntero a la lista, solo para el primer caso de si funcion_filter es false
					; y tengo que borrar el primer nodo, reapuntar el l->primero al siguiente

.ciclo:	
	cmp qword R15, NULL			; guarda del bucle, mientras que tenga nodos aplico filter
	je .fin				; si es null no hay mas nodos, salgo
	mov RDI, [R15 + offset_dato]	; paso el dato como parametro
	call R12			; llamo a funcion filter
	cmp RAX, 0			; chequeo el resultado de la funcion_filter
	je .filter_false		; si dio false salto al manejo del false

.filter_true:
	mov R14, R15			; nodoAnterior = nodoActual
	mov R15, [R15 + offset_siguiente]	; nodoActual = nodoActual->siguiente
	jmp .ciclo			; vuelvo a preguntar si termine de recorrer la lista

.filter_false:
	mov R13, [R15 + offset_siguiente]	; nodoAux = nodoActual->siguiente
	cmp R14, NULL			; pregunto si nodoAnterior es NULL para saber si es el primero o no
	jne .no_es_primero		; si no es igual no es el primero
	mov [RBX], R13			; le asigno nodoActual->siguiente que es lo que tenia en nodoAux
	jmp .borrar_nodo		; hice las asignaciones correspondientes, llamo a borrar el nodoActual
		
.no_es_primero:
	lea RAX, [R14 + offset_siguiente] ; pongo en RAX nodoAnterior->siguiente
	mov [RAX], R13			; asigno como nodoAnterior->siguiente = nodoActual->siguiente

.borrar_nodo:	
	mov RDI, [R15 + offset_dato]	; paso el puntero al dato a free
	call free			; borro el dato
	mov RDI, R15			; paso el puntero al nodo a free
	call free			; borro el nodo
	mov R15, R13			; nodoActual = nodoAux que era el siguiente
	jmp .ciclo			; vuelvo a preguntar si termine de recorrer la lista


.fin:	
	pop RBX
	pop R12
	pop R13
	pop R14
	pop R15
	ret

; ============ lista  listaMap(lista l, void* (*funcion_map)(void*) )
;				    RDI		RSI
; en R15 guardo el nodoActual
; en R14 voy a guardar provisoriamente el *resultado de funcion_map
; en RBX guardo la funcion_map
listaMap:
	push R15			; impar | preservo R15
	push R14			; par | preservo R14
	push RBX			; impar | preservo RBX

	mov R15, [RDI + offset_primero]	;guardo el primer nodo en nodoActual
	mov RBX, RSI			; guardo funcion_map en RBX

.ciclo:
	cmp R15, NULL			; chequeo que no sea null el nodo
	je .fin				; si es null termine de recorrer, salgo
	mov RDI, [R15 + offset_dato]	; paso el dato como parametro de funcion_map
	call RBX			; llamo a funcion map
	mov R14, RAX			; guardo el resultado provisoriamente en R14
	mov RDI, [R15 + offset_dato]	; paso el dato como parametro de free
	call free
	lea RDI, [R15 + offset_dato]	; pongo en RDI el puntero al dato
	mov [RDI], R14			; pongo el nuevo dato como puntero al dato del nodo
	mov R15, [R15 + offset_siguiente]	; nodoActual = nodoActual->siguiente
	jmp .ciclo			; voy por el proximo nodo
	

.fin:
	pop RBX
	pop R14
	pop R15
	ret

; ============ boolean es_multiplo_de_5(int* dato)
es_multiplo_de_5:
	push RBX
	xor RDX, RDX
	xor RAX, RAX
	mov EAX, [RDI]
	mov RBX, 5
	idiv RBX
	cmp RDX, 0
	je .es_multiplo
	mov RAX, 0
	jmp .fin

.es_multiplo:
	mov RAX, 1
	
.fin:	pop RBX
	ret

; ============ boolean es_negativo(double* dato)
es_negativo:
	mov RDI, [RDI]
	shr RDI, 63
	cmp RDI, 0
	mov RAX, RDI
	ret

; ============ boolean es_largo_mayor_10(char* dato)
es_largo_mayor_10:
	sub RSP, 8
	call strlen
	add RSP, 8
	cmp qword RAX, 11
	jge .esMasLargo
	mov RAX, 0
	jmp .fin
	
.esMasLargo:
	mov RAX, 1

.fin:	ret

; ============ int* dividir_por_dos(int* dato);
dividir_por_dos:
	push RBX
	xor RBX, RBX
	mov EBX, [RDI]
	mov RDI, tamanio_int
	call malloc
	SAR EBX, 1
	mov [RAX], EBX
	pop RBX
	ret

; ============ double* multiplicar_por_pi(double* dato);
multiplicar_por_pi:
	sub RSP, 8
	pxor XMM0, XMM0
	movsd XMM0, [RDI]
	mov RDI, tamanio_double
	call malloc
	mulsd XMM0, [pi]
	movsd [RAX], XMM0
	add RSP, 8
	ret

; ============ char* tomar_primeros_10(char* dato)
tomar_primeros_10:
	push R15			; preservo R15 
	push RBX			; preservo RBX
	sub RSP, 8			; alineo la pila, la voy a restaurar al final de todo
	mov R15, RDI			; en R15 guardo el puntero al dato
	call strlen			; consulto la longitud del string
	cmp RAX, 10			; pregunto si la longitud del string es mayor a 10
	mov RBX, RAX			; guardo la longitud del string en RBX
	jl .continuar			; si es menor a 10 dejo en RBX la longitud original
	mov RBX, 10			; si es mayor pongo 10 en RBX que es de donde voy a sacar la long del nuevo str

.continuar:
	mov RDI, RBX			; le voy a pasar a malloc la longitud del nuevo string
	add RDI, 2			; agrego 1 para el '\0'
	call malloc			; pido memoria para el string de retorno
	cmp RAX, 0			; me fijo si me devolvio un puntero a null
	je .fin_error			; si fue asi me voy con error
	mov RDI, RAX			; como voy a hacer strcpy le paso el nuevo puntero a dato 
	mov RSI, R15			; y el dato original 
	mov RDX, RBX			; le paso la cantidad de caracteres que quiero copiar
	call strncpy			; copio el string
	mov RDI, RAX			; me preservo el puntero al nuevo dato en RDI
	lea RAX, [RAX + RBX]		; me posiciono al final del string
	mov byte [RAX], 0		; escribo el fin del string
	mov RAX, RDI			; me posiciono al principio del string
	jmp .fin			; ya retorne el puntero al nuevo string, me voy


.fin_error:
	mov RAX, 1

.fin:
	add RSP, 8		; restauro la pila
	pop RBX
	pop R15
	ret



